/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 * Licensed under the Apache License, Version 2.0(the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Eth_GEthMac.h"
#include "Eth_GEthMac_Cfg.h"


#define ISR_PRIORITY_GETH_TX (5u) /* Define the Ethernet transmit interrupt priority  */
#define ISR_PRIORITY_GETH_RX (6u) /* Define the Ethernet receive interrupt priority   */

extern void ethif_txconfirmation(uint8 ctrl_idx, eth_buf_idx_type buf_idx);
extern void ethif_rxindication(uint8 ctrl_idx, eth_frame_type frame_type,
	boolean is_broadcast, const uint8 *phys_addr_ptr, eth_data_type *data_ptr, uint16 len_byte);

static IfxGeth_Eth_Config geth_config;
static IfxGeth_Eth ethernetif;

static volatile uint8 channel0_tx_confirm_idx;
static volatile uint8 channel0_tx_curr_buffer_idx;
static volatile boolean geth_mac_init_status = FALSE;


static void eth_txBufferCtrl_init(void)
{
	uint8 i;

	for (i = 0; i < IFXGETH_MAX_TX_DESCRIPTORS; i++) {
		channel0_tx_buffer_crtl[i].len_req = ETH_GETHMAC_ZERO;
		channel0_tx_buffer_crtl[i].owner_status = ETH_GETHMAC_ZERO;
		channel0_tx_buffer_crtl[i].tx_confirm_req = ETH_NO_NEED_TXCONFIRMATION;
		channel0_tx_buffer_crtl[i].inited = ETH_TX_BUFFER_NO_INITED;
	}
	channel0_tx_confirm_idx = ETH_GETHMAC_ZERO;
	channel0_tx_curr_buffer_idx = ETH_GETHMAC_ZERO;
}

static inline uint8 eth_txbuf_idx_icrement(uint8 currTxIdx)
{
	return (currTxIdx + 1U) % (IFXGETH_MAX_TX_DESCRIPTORS);
}

static inline boolean eth_is_broadcast_addr(const uint8 *Maddr)
{
	if ((Maddr[0] == 0xFFU) && (Maddr[1] == 0xFFU) && (Maddr[2] == 0xFFU) && (Maddr[3] == 0xFFU) &&
		(Maddr[4] == 0xFFU) && (Maddr[5] == 0xFFU)) {
		return TRUE;
	}
	return FALSE;
}

static uint16 get_rx_frame_size(volatile IfxGeth_RxDescr *descr)
{
	uint16 len;

	uint32 rdes3 = descr->RDES3.U;
	uint32 rdes1 = descr->RDES1.U;

	if (((rdes3 & (1UL << 15)) != 0U) || ((rdes1 & (1UL << 7)) != 0U) || ((rdes3 & (1UL << 28)) == 0U)) {
		/* Error, this block is invalid */
		len = 0xFFFFU;
	} else {
		/* Subtract CRC */
		len = (rdes3 & 0x7FFF) - 4U;
	}

	return len;
}

static void ifxgeth_mac_enable_all_txdescr_interrupt_mode(IfxGeth_Eth *geth, IfxGeth_DmaChannel channelId)
{
	volatile IfxGeth_TxDescr *tx_descr_ptr = IfxGeth_Eth_getBaseTxDescriptor(&ethernetif, ETH_RX_DMA_CHANNEL_MAP);

	for (uint8 i = 0; i < IFXGETH_MAX_TX_DESCRIPTORS; i++) {
		tx_descr_ptr[i].TDES2.R.IOC = 1; /* enable interrupt mode */
	}
}

extern void SuspendAllInterrupts(void);
extern void ResumeAllInterrupts(void);

static void eth_irq_save(void)
{
	SuspendAllInterrupts();
}

static void eth_irq_enable(void)
{
	ResumeAllInterrupts();
}

void eth_gethmac_init(void)
{
	IfxGeth_Eth_initModuleConfig(&geth_config, &MODULE_GETH);

	eth_txBufferCtrl_init();

	/* this is our PHY */
	geth_config.phyInterfaceMode = IfxGeth_PhyInterfaceMode_rgmii;
	geth_config.pins.rgmiiPins = &gethmac_pins;
	/* MAC core configuration */
	geth_config.mac.lineSpeed = ETH_LINK_SPEED;
	geth_config.mac.loopbackMode = ETH_LOOP_BACK_MODE;
	geth_config.mac.macAddress[0] = (ETH_MAC_ADDRESS >> (ETH_GETHMAC_FIVE * ETH_GETHMAC_SHIFT_8BIT)) & 0xFF;
	geth_config.mac.macAddress[1] = (ETH_MAC_ADDRESS >> (ETH_GETHMAC_FOUR * ETH_GETHMAC_SHIFT_8BIT)) & 0xFF;
	geth_config.mac.macAddress[2] = (ETH_MAC_ADDRESS >> (ETH_GETHMAC_THREE * ETH_GETHMAC_SHIFT_8BIT)) & 0xFF;
	geth_config.mac.macAddress[3] = (ETH_MAC_ADDRESS >> (ETH_GETHMAC_TWO * ETH_GETHMAC_SHIFT_8BIT)) & 0xFF;
	geth_config.mac.macAddress[4] = (ETH_MAC_ADDRESS >> (ETH_GETHMAC_ONE * ETH_GETHMAC_SHIFT_8BIT)) & 0xFF;
	geth_config.mac.macAddress[5] = (ETH_MAC_ADDRESS >> (ETH_GETHMAC_ZERO * ETH_GETHMAC_SHIFT_8BIT)) & 0xFF;

	/* MTL configuration */
	geth_config.mtl.numOfTxQueues = ETH_NUM_TX_QUEUES;
	geth_config.mtl.numOfRxQueues = ETH_NUM_RX_QUEUES;
	geth_config.mtl.txQueue[0].txQueueSize = ETH_TX_QUEUE_SIZE;
	geth_config.mtl.txQueue[0].storeAndForward = TRUE;
	geth_config.mtl.rxQueue[0].rxQueueSize = ETH_RX_QUEUE_SIZE;
	geth_config.mtl.rxQueue[0].rxDmaChannelMap = ETH_RX_DMA_CHANNEL_MAP;
	geth_config.mtl.rxQueue[0].storeAndForward = TRUE;

	/* DMA configuration */
	geth_config.dma.numOfTxChannels = ETH_NUM_TX_CHANNELS;
	geth_config.dma.numOfRxChannels = ETH_NUM_RX_CHANNELS;
	geth_config.dma.txChannel[0].channelId = ETH_TX_DMA_CHANNEL_MAP;
	geth_config.dma.txChannel[0].txDescrList = (IfxGeth_TxDescrList *)&IfxGeth_Eth_txDescrList[0];
	geth_config.dma.txChannel[0].txBuffer1StartAddress = (uint32 *)&channel0_tx_buffer[0][0]; /* user buffer */
	geth_config.dma.txChannel[0].txBuffer1Size =
		IFXGETH_MAX_TX_BUFFER_SIZE; /* used to calculate the next descriptor  buffer offset */

	geth_config.dma.rxChannel[0].channelId = ETH_RX_DMA_CHANNEL_MAP;
	geth_config.dma.rxChannel[0].rxDescrList = (IfxGeth_RxDescrList *)&IfxGeth_Eth_rxDescrList[0];
	geth_config.dma.rxChannel[0].rxBuffer1StartAddress = (uint32 *)&channel0_rx_buffer[0][0]; /* user buffer */
	geth_config.dma.rxChannel[0].rxBuffer1Size = IFXGETH_MAX_RX_BUFFER_SIZE; /* user defined variable */

	IfxSrc_Tos geth_isr_provider;

	if (CPU_WHICH_SERVICE_ETHERNET) {
		geth_isr_provider = (IfxSrc_Tos)(CPU_WHICH_SERVICE_ETHERNET + 1u);
	} else {
		geth_isr_provider = (IfxSrc_Tos)CPU_WHICH_SERVICE_ETHERNET;
	}

	geth_config.dma.txInterrupt[0].channelId = ETH_DMA_CHANNEL_ID;
	geth_config.dma.txInterrupt[0].priority = ISR_PRIORITY_GETH_TX;
	geth_config.dma.txInterrupt[0].provider = geth_isr_provider;
	geth_config.dma.rxInterrupt[0].channelId = ETH_DMA_CHANNEL_ID;
	geth_config.dma.rxInterrupt[0].priority = ISR_PRIORITY_GETH_RX;
	geth_config.dma.rxInterrupt[0].provider = geth_isr_provider;

	/* first we reset our phy manually, to make sure that the phy is ready when we init our module */
	IfxGeth_enableModule(&MODULE_GETH);
	IfxPort_setPinModeOutput(ETH_MDC_PIN.pin.port, ETH_MDC_PIN.pin.pinIndex,
		IfxPort_OutputMode_pushPull, ETH_MDC_PIN.select);
	GETH_GPCTL.B.ALTI0 = ETH_MDIO_PIN.inSelect;

	while (GETH_MAC_MDIO_ADDRESS.B.GB) {
	};
	/* first we wait that we are able to communicate with the Phy */
	do {
		GETH_MAC_MDIO_ADDRESS.U =
			(ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_21BIT) | (ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_16BIT) |
			(ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_8BIT) | (ETH_GETHMAC_THREE << ETH_GETHMAC_TWO) |
			(ETH_GETHMAC_ONE << ETH_GETHMAC_ZERO);
		while (GETH_MAC_MDIO_ADDRESS.B.GB) {
		};
	} while (GETH_MAC_MDIO_DATA.U & 0x8000); /* wait for reset to finish */
	/* reset PHY */
	GETH_MAC_MDIO_DATA.U = 0x8000;
	GETH_MAC_MDIO_ADDRESS.U = (ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_21BIT) |
				(ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_16BIT) |
				(ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_8BIT) | (ETH_GETHMAC_ONE << ETH_GETHMAC_TWO) |
				(ETH_GETHMAC_ONE << ETH_GETHMAC_ZERO);
	while (GETH_MAC_MDIO_ADDRESS.B.GB) {
	};

	do {
		GETH_MAC_MDIO_ADDRESS.U =
			(ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_21BIT) | (ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_16BIT) |
			(ETH_GETHMAC_ZERO << ETH_GETHMAC_SHIFT_8BIT) | (ETH_GETHMAC_THREE << ETH_GETHMAC_TWO) |
			(ETH_GETHMAC_ONE << ETH_GETHMAC_ZERO);
		while (GETH_MAC_MDIO_ADDRESS.B.GB) {
		};
	} while (GETH_MAC_MDIO_DATA.U & 0x8000); /* wait for reset to finish */

	IfxGeth_Eth_initModule(&ethernetif, &geth_config);
	IfxGeth_mac_setAllMulticastPassing(ethernetif.gethSFR, TRUE);
	ifxgeth_mac_enable_all_txdescr_interrupt_mode(&ethernetif, ETH_RX_DMA_CHANNEL_MAP);

	phy_init_func_ptr();

	/* and enable transmitter/receiver */
	IfxGeth_Eth_startTransmitters(&ethernetif, 1);
	IfxGeth_Eth_startReceivers(&ethernetif, 1);
}

BufReq_ReturnType ETH_ProvideTxBuffer(const uint8 ctrl_idx, uint8 priority, eth_buf_idx_type *const bufidx_ptr,
	uint8 **const buf_ptr, uint16 *const lenbyte_ptr)
{
	(void)ctrl_idx;
	(void)priority;
	volatile eth_tx_buffer_crtl_type *tx_buff_crtr_ptr = NULL;
	volatile IfxGeth_TxDescr *tx_descr_ptr = NULL;
	BufReq_ReturnType buffer_status = BUFREQ_E_BUSY;
	*buf_ptr = NULL;

	if ((*lenbyte_ptr) > (IFXGETH_MAX_TX_BUFFER_SIZE - (uint16)(ETH_FRAMEHEADER_LENGTH + ETH_FRAMECHKSUM_LENGTH))) {
		*lenbyte_ptr = IFXGETH_MAX_TX_BUFFER_SIZE - (uint16)(ETH_FRAMEHEADER_LENGTH + ETH_FRAMECHKSUM_LENGTH);
		buffer_status = BUFREQ_E_OVFL;
	}

	eth_irq_save();
	tx_buff_crtr_ptr = &channel0_tx_buffer_crtl[channel0_tx_curr_buffer_idx];
	tx_descr_ptr = IfxGeth_Eth_getBaseTxDescriptor(&ethernetif, ETH_RX_DMA_CHANNEL_MAP);

	if (tx_buff_crtr_ptr->owner_status == ETH_BUFFER_NOT_USED) {
		*bufidx_ptr = channel0_tx_curr_buffer_idx;

		tx_buff_crtr_ptr->owner_status = ETH_BUFFER_USED;
		tx_buff_crtr_ptr->inited = ETH_TX_BUFFER_NO_INITED;
		tx_buff_crtr_ptr->tx_confirm_req = ETH_NO_NEED_TXCONFIRMATION;
		tx_buff_crtr_ptr->len_req = *lenbyte_ptr;
		*buf_ptr = (uint8 *)(tx_descr_ptr[channel0_tx_curr_buffer_idx].TDES0.U + ETH_FRAMEHEADER_LENGTH);
		channel0_tx_curr_buffer_idx = eth_txbuf_idx_icrement(channel0_tx_curr_buffer_idx);
		buffer_status = BUFREQ_OK;
	}

	eth_irq_enable();
	return buffer_status;
}

static void eth_tx_dma_ch0(void)
{
	volatile eth_tx_buffer_crtl_type *tx_buffer_ctrl_ptr;
	uint32 desc_status;
	uint8 i;
	volatile IfxGeth_TxDescr *tx_descr_ptr = NULL;

	if (geth_mac_init_status != TRUE) {
		return;
	}

	tx_descr_ptr = IfxGeth_Eth_getBaseTxDescriptor(&ethernetif, ETH_RX_DMA_CHANNEL_MAP);

	for (i = 0; i < IFXGETH_MAX_TX_DESCRIPTORS; i++) {
		desc_status = tx_descr_ptr[channel0_tx_confirm_idx].TDES3.U;
		tx_buffer_ctrl_ptr = &channel0_tx_buffer_crtl[channel0_tx_confirm_idx];

		if ((tx_buffer_ctrl_ptr->owner_status == ETH_BUFFER_USED) && (tx_buffer_ctrl_ptr->inited == ETH_GETHMAC_ONE) &&
			((desc_status & (uint32)ETH_TX_DESC_OWN_SET) == ETH_GETHMAC_ZERO)) {
			if (tx_buffer_ctrl_ptr->tx_confirm_req == ETH_NEED_TXCONFIRMATION) {
				ethif_txconfirmation(0, (eth_buf_idx_type)channel0_tx_confirm_idx);
				tx_buffer_ctrl_ptr->tx_confirm_req = ETH_NO_NEED_TXCONFIRMATION;
			}

			tx_buffer_ctrl_ptr->inited = ETH_TX_BUFFER_NO_INITED;
			tx_buffer_ctrl_ptr->owner_status = ETH_BUFFER_NOT_USED;
			channel0_tx_confirm_idx = eth_txbuf_idx_icrement(channel0_tx_confirm_idx);
		} else {
			break;
		}
	}
}

Std_ReturnType ETH_Transmit(uint8 ctrl_idx, eth_buf_idx_type buf_idx, eth_frame_type frame_type,
	boolean tx_confirmation, uint16 len_byte, const uint8 *phys_addr_ptr)
{
	(void)ctrl_idx;
	IfxGeth_Eth *eth_ctrl = &ethernetif;
	uint8 *tx_buf = NULL;
	uint32 tx_length = 0u;
	volatile IfxGeth_TxDescr *tx_descr_ptr = NULL;

	eth_irq_save();
	tx_descr_ptr = IfxGeth_Eth_getBaseTxDescriptor(&ethernetif, ETH_RX_DMA_CHANNEL_MAP);
	tx_buf = (uint8 *)(tx_descr_ptr[buf_idx].TDES0.U);

	if (tx_confirmation == (boolean)TRUE) {
		channel0_tx_buffer_crtl[buf_idx].tx_confirm_req = ETH_NEED_TXCONFIRMATION;
	} else {
		channel0_tx_buffer_crtl[buf_idx].tx_confirm_req = ETH_NO_NEED_TXCONFIRMATION;
	}

	/*set dest mac address*/
	for (uint8 i = 0u; i < (uint8)ETH_MACADDRESS_LEN; i++) {
		tx_buf[i] = (uint8)phys_addr_ptr[i];
	}

	/* set source mac address */
	for (uint8 j = 0u; j < (uint8)ETH_MACADDRESS_LEN; j++) {
		tx_buf[ETH_MACADDRESS_LEN + j] = (uint8)geth_config.mac.macAddress[j];
	}

	/* set frame type */
	tx_buf[ETH_FRAME_TYPE_POS_MSB] = (uint8)((frame_type >> ETH_FRAME_SHIFT_8BIT) & ETH_FRAME_BYTE_MASK);
	tx_buf[ETH_FRAME_TYPE_POS_LSB] = (uint8)(frame_type & ETH_FRAME_BYTE_MASK);
	tx_length = (uint32)(((uint32)len_byte + ETH_FRAMEHEADER_LENGTH) & (uint32)ETHC_DMA_TX_DESC_PAC_LEN_MASK);

	tx_descr_ptr[buf_idx].TDES3.U = tx_length;
	tx_descr_ptr[buf_idx].TDES3.U |= (uint32)ETH_TX_DESC_FD_SET;
	tx_descr_ptr[buf_idx].TDES2.U =
		((uint32)(tx_descr_ptr[buf_idx].TDES2.U & ((uint32)ETH_MASK_TDES2_BUFLENGTH))) | ((uint32)(tx_length));
	tx_descr_ptr[buf_idx].TDES3.U |= (uint32)ETH_TX_DESC_LD_SET;
	tx_descr_ptr[buf_idx].TDES3.U |= (uint32)(ETH_TX_DESC_OWN_SET);
	channel0_tx_buffer_crtl[buf_idx].inited = ETH_TX_BUFFER_INITED;
	IfxGeth_dma_setTxDescriptorTailPointer(eth_ctrl->gethSFR, ETH_DMA_CHANNEL_ID,
						(uint32)(&tx_descr_ptr[IFXGETH_MAX_TX_DESCRIPTORS]));
	eth_irq_enable();
	return E_OK;
}

void ETH_GetPhysAddr(uint8 ctrl_idx, uint8 *phys_addr_ptr)
{
	(void)ctrl_idx;
	if (phys_addr_ptr) {
		for (uint8 i = 0u; i < (uint8)ETH_MACADDRESS_LEN; i++) {
			phys_addr_ptr[i] = (uint8)geth_config.mac.macAddress[i];
		}
	}
}

static inline boolean IfxGeth_dma_ReceiveInterruptAvailable(IfxGeth_Eth *geth, IfxGeth_DmaChannel channelId)
{
	return (geth->gethSFR->DMA_CH[channelId].STATUS.B.RI == 1u);
}

static inline boolean IfxGeth_dma_ReceiveBufferUnavaible(IfxGeth_Eth *geth, IfxGeth_DmaChannel channelId)
{
	return (geth->gethSFR->DMA_CH[channelId].STATUS.B.RBU == 1u);
}

static inline void IfxGeth_reset_dma_status(IfxGeth_Eth *geth, IfxGeth_DmaChannel channelId)
{
	geth->gethSFR->DMA_CH[channelId].STATUS.U =
		((uint32)ETH_DMA_CH_STAT_RI_RESET | (uint32)ETH_DMA_CH_STAT_NIS_RESET |
		(uint32)ETH_DMA_CH_STAT_RBU_ERI_AIE_RESET);
}

static inline void IfxGeth_reset_dma_rx_tail(IfxGeth_Eth *geth, IfxGeth_DmaChannel channelId)
{
	volatile IfxGeth_RxDescr *RxDescrPtr = IfxGeth_Eth_getBaseRxDescriptor(geth, (IfxGeth_RxDmaChannel)channelId);

	IfxGeth_dma_setRxDescriptorTailPointer(geth->gethSFR, (IfxGeth_RxDmaChannel)channelId,
						(uint32)&RxDescrPtr[IFXGETH_MAX_RX_DESCRIPTORS]);
}

void eth_rx_dma_ch0(void)
{
	uint16 len_byte = 0u;
	eth_frame_type frame_type = 0x800u; /* IPv4 */
	uint8 ctrl_idx = 0u;
	boolean is_broadcast = FALSE;
	const uint8 *phys_addr_ptr = NULL;
	eth_data_type *data_ptr = NULL;

	uint8 rx_dma_channel = ETH_RX_DMA_CHANNEL_MAP;
	IfxGeth_Eth *eth_ctrl = &ethernetif;
	volatile IfxGeth_RxDescr *descr = NULL;
	uint8 *current_buffer_ptr = NULL;
	uint8 max_loop = 0u;

	if (geth_mac_init_status != TRUE) {
		return;
	}

	while ((IfxGeth_dma_ReceiveInterruptAvailable(eth_ctrl, rx_dma_channel)) ||
		(IfxGeth_dma_ReceiveBufferUnavaible(eth_ctrl, rx_dma_channel))) {
		IfxGeth_reset_dma_status(eth_ctrl, rx_dma_channel);

		while (IfxGeth_Eth_isRxDataAvailable(eth_ctrl, rx_dma_channel) == TRUE) {
			descr = IfxGeth_Eth_getActualRxDescriptor(eth_ctrl, rx_dma_channel);
			len_byte = get_rx_frame_size(descr);
			if (len_byte == 0) {
				break;
			}

			current_buffer_ptr = IfxGeth_Eth_getReceiveBuffer(eth_ctrl, rx_dma_channel);
			if (current_buffer_ptr == NULL) {
				break;
			}

			is_broadcast = eth_is_broadcast_addr(&(current_buffer_ptr[ETH_FRAME_DESTADDR_POS]));
			frame_type = ((uint16)(((uint16)(((uint16)current_buffer_ptr[ETH_FRAME_TYPE_POS_MSB])
							<< (ETH_FRAME_SHIFT_8BIT))) |
						((uint16)current_buffer_ptr[ETH_FRAME_TYPE_POS_LSB])));
			phys_addr_ptr = &(current_buffer_ptr[ETH_FRAME_SRCADDR_POS]);
			data_ptr = &(current_buffer_ptr[ETH_FRAME_PAYLOAD_POS]);

			(void)ethif_rxindication(ctrl_idx, frame_type, is_broadcast, phys_addr_ptr, data_ptr, len_byte);
			IfxGeth_Eth_freeReceiveBuffer(eth_ctrl, rx_dma_channel);
		}

		IfxGeth_reset_dma_rx_tail(eth_ctrl, rx_dma_channel);

		max_loop++;
		if (max_loop > IFXGETH_RX_INT_MAXLOOPCNT) {
			break;
		}
	}
}

void eth_enable_eth_mac(void)
{
	geth_mac_init_status = TRUE;
}

void ethisr2_isr(void)
{
	eth_tx_dma_ch0();
}

void ethisr6_isr(void)
{
	eth_rx_dma_ch0();
}
